/*
 * Copyright 2011-2012 Rivoli team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.rivoli.model.classhierarchy

import com.googlecode.rivoli.RivoliException

import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class Utilities {
	static Class<?> findContainedType(Class<?> elementDescriptorClass, boolean includeAncestors) {
		Class<?> containedType = null

		Field containerField = findContainerField(elementDescriptorClass, includeAncestors)

		if (containerField) {
			if (containerField.type == List) {
				Type fieldType = containerField.genericType

				if (fieldType in ParameterizedType) {
					containedType = getTypeArgument(fieldType, 0)
				}
			}

			if (!containedType) {
				throw new RivoliException("container field '${containerField.name}' of class '${elementDescriptorClass.simpleName}' is not a parameterized list")
			}
		}

		containedType
	}

	static Class<?> getTypeArgument(ParameterizedType parameterisedType, int index) {
		Class<?> typeArgument = null

		Type typeArgumentAsType = parameterisedType.actualTypeArguments[index]

		if (typeArgumentAsType instanceof Class) {
			typeArgument = typeArgumentAsType
		}

		if (!typeArgument) {
			throw new RivoliException("type argument ${index} for '${parameterisedType.rawType}' is not a class")
		}

		typeArgument
	}

	static Field findContainerField(Class<?> elementDescriptorClass, boolean includeAncestors) {
		Collection<Field> containerFields = getFields(elementDescriptorClass, includeAncestors).findAll {it.isAnnotationPresent(Children)}

		if (containerFields.size() > 1) {
			throw new RivoliException("element descriptor class '${elementDescriptorClass.simpleName}' should not declare multiple child containers")
		}

		(containerFields.size() == 1) ? containerFields[0] : null
	}

	private static List<Field> getFields(Class<?> clazz, boolean includeAncestors) {
		List<Field> fields = []

		Class<?> currentClass = clazz

		while (currentClass) {
			fields.addAll(currentClass.declaredFields as List)

			currentClass = includeAncestors ? currentClass.superclass : null
		}

		fields
	}
}